import React, { useEffect, useRef, useState } from 'react';
import { usePersistFn } from 'ahooks';
import html2canvas from 'html2canvas';
import './index.less';

const height500 = 500;
const defaultEffRatio = 0.5;
const stepY = 25;

export interface RCodeNavProps {
  bindNode: HTMLElement | null;
  editor: any;
}

const RCodeNav: React.FC<RCodeNavProps> = function({ bindNode, editor }) {
  const navWrap = useRef(null);
  const canvasRef = useRef(null);
  const handlerRef = useRef(null);
  const [canvas, setCanvas] = useState();

  useEffect(() => {
    handlerRefInit();
    initNavWrapScroll();
    initCanvas();
    return () => {
      endNavWrapScroll();
    };
  }, []);

  useEffect(() => {
    if (!editor) return;
    editor.on('change', () => {
      turnHtml2Canvas();
    });
  }, [editor]);

  useEffect(() => {
    if (!bindNode) return;
    attachScrollEvent();
    turnHtml2Canvas();
    return () => {
      removEScrollEvent();
    };
  }, [bindNode]);

  const turnHtml2Canvas = () => {
    const scrollContentDom = bindNode?.querySelector('.CodeMirror-sizer') as any;
    const scrollDom = bindNode?.querySelector('.CodeMirror-scroll') as any;
    (canvas as any).width = scrollDom.offsetWidth - 50;
    (canvas as any).height = scrollDom.offsetHeight * 8;
    html2canvas(scrollContentDom, {
      canvas,
      backgroundColor: 'transparent',
    });
  };

  const handlerRefInit = usePersistFn(() => {
    if (!handlerRef.current) return;
    let offsetY = 0;
    const handlerDom = handlerRef.current as any;
    handlerDom.addEventListener('mousedown', mouseDown);
    function mouseDown(e: any) {
      offsetY = e.clientY;
      window.addEventListener('mousemove', mouseMove);
      window.addEventListener('mouseup', mouseUp);
    }
    function mouseMove(e: any) {
      const top = parseInt(handlerDom.style.top, 10) || 0;
      const targetY = Math.min(
        Math.max(top + (e.clientY - offsetY), 0),
        (navWrap.current as any).offsetHeight - handlerDom.offsetHeight,
      );
      if (handlerMoveAction(top, targetY - top)) {
        handlerDom.style.top = targetY + 'px';
        offsetY = e.clientY;
      }
    }
    function mouseUp() {
      window.removeEventListener('mousemove', mouseMove);
      window.removeEventListener('mouseup', mouseUp);
    }
  });
  //  CodeMirror-sizer 高度确定 scorll 范围
  const getScrollContentHeight = usePersistFn(() => {
    const scrollContentDom = bindNode?.querySelector('.CodeMirror-sizer') as any;
    if (!scrollContentDom) return 0;
    return scrollContentDom.offsetHeight;
  });

  const checkScrollMove = usePersistFn((offsetHeight, scrollTop) => {
    return offsetHeight - 150 + scrollTop < getScrollContentHeight();
  });

  const handlerMoveAction = usePersistFn((top: number, diffY: number) => {
    const navDom = navWrap.current as any;
    const ratio = navDom.offsetHeight / getScrollContentHeight();
    const scrollTop = (top + diffY) / ratio;
    const scrollDom = bindNode?.querySelector('.CodeMirror-scroll') as any;
    if (checkScrollMove(scrollDom.offsetHeight, scrollTop)) {
      scrollDom.scrollTop = scrollTop;
      return true;
    }
    return false;
  });

  // codemirror-scroll 滚动事件绑定
  const attachScrollEvent = usePersistFn(() => {
    const scrollDom = bindNode?.querySelector('.CodeMirror-scroll') as any;
    if (scrollDom) scrollDom.addEventListener('scroll', editorScroll);
  });

  const removEScrollEvent = usePersistFn(() => {
    const scrollDom = bindNode?.querySelector('.CodeMirror-scroll') as any;
    if (scrollDom) scrollDom.removeEventListener('scroll', editorScroll);
  });

  const editorScroll = (e: any) => {
    const scrollDom = e.target;
    const scrollTop = scrollDom.scrollTop;
    const handlerDom = handlerRef.current as any;
    const navDom = navWrap.current as any;
    const ratio = navDom.offsetHeight / getScrollContentHeight();
    const targetY = scrollTop * ratio;
    handlerDom.style.top = targetY + 'px';
  };

  // 导航滚动事件绑定
  const initNavWrapScroll = () => {
    const wrapNode = navWrap.current as any;
    if (wrapNode) wrapNode.addEventListener('mousewheel', navWrapMouseWheel);
  };

  const endNavWrapScroll = () => {
    const wrapNode = navWrap.current as any;
    if (wrapNode) wrapNode.removeEventListener('mousewheel', navWrapMouseWheel);
  };

  const navWrapMouseWheel = usePersistFn(e => {
    const scrollDom = bindNode?.querySelector('.CodeMirror-scroll') as any;
    const scrollTop = scrollDom.scrollTop;
    const { wheelDelta } = e;
    if (wheelDelta > 0) {
      scrollDom.scrollTop = scrollTop - stepY;
    } else {
      scrollDom.scrollTop = scrollTop + stepY;
    }
  });

  // 初始化canvas
  const initCanvas = () => {
    const canvasDom = canvasRef.current as any;
    if (canvasDom) {
      setCanvas(canvasDom);
    }
  };

  return (
    <div className="ra-code-nav" ref={navWrap}>
      <canvas className="ra-code-canvas" ref={canvasRef} />
      <div className="ra-code-nav-handler" ref={handlerRef}></div>
    </div>
  );
};

export default RCodeNav;
